home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 52
/
Aminet 52 (2002)(GTI - Schatztruhe)[!][Dec 2002].iso
/
Aminet
/
game
/
think
/
AmiChess.lha
/
AmiChess
/
src
/
book.c
< prev
next >
Wrap
C/C++ Source or Header
|
2002-10-31
|
10KB
|
429 lines
#include <clib/alib_protos.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "common.h"
#include "book.h"
#define MAXMOVES 200
#define MAXMATCH 100
static int bookcnt;
static HashType posshash[MAXMOVES];
static int book_allocated=0;
#define MAGIC_LENGTH 5
static const char magic_str[]="\x42\x23\x08\x15\x03";
static int check_magic(FILE *f)
{
char buf[MAGIC_LENGTH];
return fread(&buf,1,MAGIC_LENGTH,f)==MAGIC_LENGTH&&memcmp(buf,magic_str,MAGIC_LENGTH)==0;
}
static int write_magic(FILE *f)
{
return (fwrite(&magic_str,1,MAGIC_LENGTH,f)!=MAGIC_LENGTH)?BOOK_EIO:BOOK_SUCCESS;
}
static int write_size(FILE *f,unsigned int size)
{
unsigned char sizebuf[4];
int k;
for(k=0;k<4;k++) sizebuf[k]=(size>>((3-k)*8))&0xff;
if(fwrite(&sizebuf,sizeof(sizebuf),1,f)==1) return BOOK_SUCCESS;
return BOOK_EIO;
}
static unsigned int read_size(FILE *f)
{
unsigned char sizebuf[4];
unsigned int size=0;
int k;
if(fread(&sizebuf,sizeof(sizebuf),1,f)!=1) return 0;
for(k=0;k<4;k++) size=(size<<8)|sizebuf[k];
return size;
}
#define MAX_DIGEST_BITS 20
static int digest_bits;
#define DIGEST_SIZE (1UL<<digest_bits)
#define DIGEST_MASK (DIGEST_SIZE-1)
static struct hashtype
{
unsigned short wins;
unsigned short losses;
unsigned short draws;
HashType key;
} *bookpos;
#ifdef __STORM__
__inline int is_empty(unsigned int index)
#else
static inline int is_empty(unsigned int index)
#endif
{
return bookpos[index].key==0&&bookpos[index].wins==0&&bookpos[index].draws==0&&bookpos[index].losses==0;
}
#define DIGEST_START(i,key) ((i)=(key)&DIGEST_MASK)
#define DIGEST_MATCH(i,the_key) ((the_key)==bookpos[i].key)
#define DIGEST_EMPTY(i)is_empty(i)
#define DIGEST_COLLISION(i,key) (!DIGEST_MATCH(i,key)&&!DIGEST_EMPTY(i))
#define DIGEST_NEXT(i,key) ((i)=((i)+(((key)>>digest_bits)|1))&DIGEST_MASK)
static int bookhashcollisions=0;
#define DIGEST_LIMIT (0.95*DIGEST_SIZE)
static unsigned char buf[2+2+2+8];
static const int wins_off=0;
static const int losses_off=2;
static const int draws_off=4;
static const int key_off=6;
static void buf_to_book()
{
HashType key;
unsigned int i;
key=((unsigned long long)buf[key_off]<<56)|
((unsigned long long)buf[key_off+1]<<48)|
((unsigned long long)buf[key_off+2]<<40)|
((unsigned long long)buf[key_off+3]<<32)|
((unsigned long long)buf[key_off+4]<<24)|
((unsigned long long)buf[key_off+5]<<16)|
((unsigned long long)buf[key_off+6]<<8)|
((unsigned long long)buf[key_off+7]);
for(DIGEST_START(i,key);DIGEST_COLLISION(i,key);DIGEST_NEXT(i,key)) bookhashcollisions++;
bookpos[i].wins+=(buf[wins_off]<<8)|buf[wins_off+1];
bookpos[i].draws+=(buf[draws_off]<<8)|buf[draws_off+1];
bookpos[i].losses+=(buf[losses_off]<<8)|buf[losses_off+1];
bookpos[i].key=key;
}
static void book_to_buf(unsigned int index)
{
int k;
for(k=0;k<2;k++)
{
int k1=(1-k)*8;
buf[wins_off+k]=(bookpos[index].wins>>k1)&0xff;
buf[draws_off+k]=(bookpos[index].draws>>k1)&0xff;
buf[losses_off+k]=(bookpos[index].losses>>k1)&0xff;
}
for(k=0;k<8;k++) buf[key_off+k]=((bookpos[index].key)>>((7-k)*8))&0xff;
}
static int compare(const void *aa,const void *bb)
{
int ret;
const leaf *a=(const leaf *)aa;
const leaf *b=(const leaf *)bb;
if(b->score>a->score) ret=1;
else if(b->score<a->score) ret=-1;
else ret=0;
return ret;
}
static int read_book(FILE *f)
{
if(book_allocated)
{
free(bookpos);
book_allocated=0;
}
bookpos=(struct hashtype *)calloc(DIGEST_SIZE,sizeof(struct hashtype));
if(!bookpos) return BOOK_ENOMEM;
book_allocated=1;
bookcnt=0;
bookhashcollisions=0;
while(fread(&buf,sizeof(buf),1,f)==1)
{
buf_to_book();
bookcnt++;
}
return BOOK_SUCCESS;
}
int BookBuilderOpen()
{
FILE *rfp,*wfp;
int res;
char text[100];
if(rfp=fopen(BOOKRUN,"rb"))
{
DoMethod(mui_app,MUIM_Chess_ShowThinking,"Opened existing book!");
if(!check_magic(rfp))
{
/* fprintf(stderr,"File %s does not conform to the current format.\nConsider rebuilding your book.\n",BOOKRUN); */
return BOOK_EFORMAT;
}
digest_bits=MAX_DIGEST_BITS;
read_size(rfp);
res=read_book(rfp);
fclose(rfp);
if(res!=BOOK_SUCCESS)
{
fclose(rfp);
return res;
}
sprintf(text,"Read %d book positions. Got %d hash collisions.",bookcnt,bookhashcollisions);
DoMethod(mui_app,MUIM_Chess_ShowThinking,text);
}
else
{
wfp=fopen(BOOKRUN,"w+b");
if(wfp==NULL)
{
/* fprintf(stderr,"Could not create %s file: %s\n",BOOKRUN,strerror(errno)); */
return BOOK_EIO;
}
if(write_magic(wfp)!=BOOK_SUCCESS)
{
/* fprintf(stderr,"Could not write to %s: %s\n",BOOKRUN,strerror(errno)); */
return BOOK_EIO;
}
if(fclose(wfp))
{
/* fprintf(stderr,"Could not write to %s: %s\n",BOOKRUN,strerror(errno)); */
return BOOK_EIO;
}
sprintf(text,"Created new book %s!",BOOKRUN);
DoMethod(mui_app,MUIM_Chess_ShowThinking,text);
if(!(rfp=fopen(BOOKRUN,"rb")))
{
/* fprintf(stderr,"Could not open %s for reading: %s\n",BOOKRUN,strerror(errno)); */
return BOOK_EIO;
}
digest_bits=MAX_DIGEST_BITS;
if(read_book(wfp)==BOOK_ENOMEM) return BOOK_ENOMEM;
}
return BOOK_SUCCESS;
}
int BookBuilder(short result,short side)
{
unsigned int i;
if(GameCnt/2+1>=BOOKDEPTH) return BOOK_EMIDGAME;
CalcHashKey();
for(DIGEST_START(i,HashKey);;DIGEST_NEXT(i,HashKey))
{
if(DIGEST_MATCH(i,HashKey))
{
existpos++;
break;
}
else if(DIGEST_EMPTY(i))
{
if(bookcnt>DIGEST_LIMIT) return BOOK_EFULL;
bookpos[i].key=HashKey;
newpos++;
bookcnt++;
break;
}
else bookhashcollisions++;
}
if(side==white)
{
if(result==R_WHITE_WINS) bookpos[i].wins++;
else if(result==R_BLACK_WINS)
bookpos[i].losses++;
else if(result==R_DRAW) bookpos[i].draws++;
}
else
{
if(result==R_WHITE_WINS) bookpos[i].losses++;
else if(result==R_BLACK_WINS) bookpos[i].wins++;
else if(result==R_DRAW) bookpos[i].draws++;
}
return BOOK_SUCCESS;
}
int BookBuilderClose()
{
FILE *wfp;
unsigned int i;
int errcode=BOOK_SUCCESS;
char text[100];
if(!(wfp=fopen(BOOKRUN,"wb")))
{
errcode=BOOK_EIO;
goto bailout_noclose;
}
if(write_magic(wfp)!=BOOK_SUCCESS)
{
errcode=BOOK_EIO;
goto bailout;
}
if(write_size(wfp,bookcnt)!=BOOK_SUCCESS)
{
errcode=BOOK_EIO;
goto bailout;
}
for(i=0;i<DIGEST_SIZE;i++)
{
if(!is_empty(i))
{
book_to_buf(i);
if(fwrite(&buf,sizeof(buf),1,wfp)!=1)
{
errcode=BOOK_EIO;
goto bailout;
}
}
}
sprintf(text,"Got %d hash collisions.",bookhashcollisions);
DoMethod(mui_app,MUIM_Chess_ShowThinking,text);
bailout:
if(fclose(wfp)) errcode=BOOK_EIO;
bailout_noclose:
free(bookpos);
book_allocated=0;
bookloaded=0;
return errcode;
}
int BookQuery()
{
int i,j,k,icnt=0,mcnt,found,tot,maxdistribution;
int matches[MAXMATCH] ;
leaf m[MAXMOVES];
leaf pref[MAXMOVES];
struct {
unsigned short wins;
unsigned short losses;
unsigned short draws;
} r[MAXMOVES];
FILE *rfp;
leaf *p;
short side,xside,temp;
unsigned int booksize;
int res;
char text[100];
if(bookloaded&&!book_allocated) return BOOK_ENOBOOK;
if(!bookloaded)
{
bookloaded=1;
if(!(rfp=fopen(BOOKBIN,"rb"))) return BOOK_ENOBOOK;
sprintf(text,"Read opening book (%s)...",BOOKBIN);
DoMethod(mui_app,MUIM_Chess_ShowThinking,text);
if(!check_magic(rfp))
{
/* fprintf(stderr," File %s does not conform to the current format.\n Consider rebuilding the book.\n\n",BOOKBIN); */
return BOOK_EFORMAT;
}
booksize=read_size(rfp)*1.06;
for(digest_bits=1;booksize;booksize>>=1) digest_bits++;
res=read_book(rfp);
if(res!=BOOK_SUCCESS) return res;
sprintf(text,"%d hash collisions... ",bookhashcollisions);
DoMethod(mui_app,MUIM_Chess_ShowThinking,text);
}
mcnt=-1;
side=board.side;
xside=1^side;
TreePtr[2]=TreePtr[1];
GenMoves(1);
FilterIllegalMoves(1);
for(p=TreePtr[1];p<TreePtr[2];p++)
{
MakeMove(side,&p->move);
m[icnt].move=p->move;
posshash[icnt]=HashKey;
icnt++;
UnmakeMove(xside,&p->move);
}
for(i=0;i<icnt;i++)
{
for(DIGEST_START(j,posshash[i]);!DIGEST_EMPTY(j);DIGEST_NEXT(j,posshash[i]))
{
if(DIGEST_MATCH(j,posshash[i]))
{
found=0;
for(k=0;k<mcnt;k++) if(matches[k]==i)
{
found=1;
break;
}
if(!found)
{
matches[++mcnt]=i;
pref[mcnt].move=m[matches[mcnt]].move;
r[i].losses=bookpos[j].losses;
r[i].wins=bookpos[j].wins;
r[i].draws=bookpos[j].draws;
pref[mcnt].score=m[i].score=100*(r[i].wins+(r[i].draws/2))/(MAX(r[i].wins+r[i].losses+r[i].draws,1))+r[i].wins/2;
}
if(mcnt>=MAXMATCH)
{
sprintf(text,"Too many matches in book.");
DoMethod(mui_app,MUIM_Chess_ShowThinking,text);
goto fini;
}
break;
}
}
}
fini:
if(mcnt==-1) return BOOK_ENOMOVES;
k=0;
if(mcnt+1)
{
if(bookmode==BOOKRAND)
{
k=rand();
k=k%(mcnt+1);
RootPV=m[matches[k]].move;
printf("\n(Random picked move #%d %s%s from above list)\n",k,algbr[FROMSQ(RootPV)],algbr[TOSQ(RootPV)]);
tot=r[matches[k]].wins+r[matches[k]].draws+r[matches[k]].losses;
if(tot) printf("B p=%2.0f\n",100.0*(r[matches[k]].wins+r[matches[k]].draws)/tot);
else printf("p=NO EXPERIENCES\n");
}
else if(bookmode==BOOKBEST)
{
qsort(&pref,mcnt+1,sizeof(leaf),compare);
RootPV=pref[0].move;
}
else if(bookmode==BOOKWORST)
{
qsort(&pref,mcnt+1,sizeof(leaf),compare);
RootPV=pref[mcnt].move;
}
else if(bookmode==BOOKPREFER)
{
qsort(&pref,mcnt+1,sizeof(leaf),compare);
for(i=0;i<=mcnt;i++)
{
m[i].move=pref[i].move;
}
temp=(bookfirstlast>mcnt+1?mcnt+1:bookfirstlast);
maxdistribution=0;
for(i=0;i<temp;i++) maxdistribution+=pref[i].score;
if(!maxdistribution) return BOOK_ENOMOVES;
k=rand()%maxdistribution;
maxdistribution=0;
for(i=0;i<temp;i++)
{
maxdistribution+=pref[i].score;
if(k>=maxdistribution-pref[i].score&&k<maxdistribution)
{
k=i;
RootPV=m[k].move;
break;
}
}
}
}
return BOOK_SUCCESS;
}